#include "P25Q80L.h" 
#include "spi.h"	
#include <stdio.h>

//init	device											 
void P25Q80L_Init(void)
{ 	 
	P25Q80L_CS_OFF;				//cs disable
	SPI1_Init();		   		//init SPI, single-spi mode
}  

 
uint8_t P25Q80L_ReadSR(void)   
{  
	uint8_t byte=0;   
	P25Q80L_CS_ON;                               
	SPI1_ReadWriteByte(P25Q80L_ReadStatusReg);    
	byte=SPI1_ReadWriteByte(0Xff);           
	P25Q80L_CS_OFF;                         
	return byte;   
} 

//P25Q80L:Write Status Register-1
/**
 * @brief Write Status Register-1.
 * @param val: value of register to be written.
 * @return This function has no return.
 */
void P25Q80L_Write_SR(uint8_t val)   
{   
	P25Q80L_CS_ON;                            
	SPI1_ReadWriteByte(P25Q80L_WriteStatusReg); 
	SPI1_ReadWriteByte(val);               	 
	P25Q80L_CS_OFF;                          
}   

//P25Q80L:Write Status Register-2
/**
 * @brief Write Status Register-2.
 * @param val: value of register to be written.
 * @return This function has no return.
 */
void P25Q80L_Write_SR2(uint8_t val)   
{   
	P25Q80L_CS_ON;           //CS en
	SPI1_ReadWriteByte(P25Q80L_WriteStatusReg2);	//sending Write Status Register-2 cmd
	SPI1_ReadWriteByte(val);   // bit1(QE)=1; enable quad-spi 
	P25Q80L_CS_OFF;          //CS dis 	      
}   


void P25Q80L_Write_Enable(void)   
{
	P25Q80L_CS_ON;                          	 
    SPI1_ReadWriteByte(P25Q80L_WriteEnable); 	 
	P25Q80L_CS_OFF;                           	 	      
} 
 
void P25Q80L_Write_Disable(void)   
{  
	P25Q80L_CS_ON;                            
    SPI1_ReadWriteByte(P25Q80L_WriteDisable);    
	P25Q80L_CS_OFF;                            	      
} 	

/*
* Read Manufacture ID/ Device ID - in Single-SPI mode
*/ 	  
uint16_t P25Q80L_SReadID(void)
{
	uint16_t Temp = 0;	  
	P25Q80L_CS_ON;				// CS enable    
	SPI1_ReadWriteByte(P25Q80L_REMS);	//send RDID cmd	
	SPI1_ReadWriteByte(DUMMY);	// dummy
	SPI1_ReadWriteByte(DUMMY);	// dummy
	SPI1_ReadWriteByte(DUMMY);	// dummy
	Temp|=SPI1_ReadWriteByte(0xFF)<<8;  // manufacturer ID
	Temp|=SPI1_ReadWriteByte(0xFF);	 	// device ID
	P25Q80L_CS_OFF;				 // CS disable  
	return Temp;
}   		    		    

/*
* Read Manufacture ID/ Device ID - in Quad-SPI mode
*/ 	  
uint16_t P25Q80L_QReadID(void)
{
	uint16_t Temp = 0;	  

	P25Q80L_Write_Enable();
	// P25Q80L_Wait_WEL();
	Enable_QE();	

	P25Q80L_CS_ON;				// CS enable    
	SPI1_ReadWriteByte(0x94);	//发�?�读取ID命令	
	qspi_switch_4line();
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00);
	// 4 dummy clocks
	SPI1_ReadWriteByte(0x00);	// dummy
	SPI1_ReadWriteByte(0x00);	// dummy
	SPI1_ReadWriteByte(0x00);	// dummy
	SPI1_ReadWriteByte(0x00);	// dummy
	Temp|=SPI1_ReadWriteByte(0xFF)<<8;  // Manufacturer ID
	Temp|=SPI1_ReadWriteByte(0xFF);	 	// Device ID
	P25Q80L_CS_OFF;				 // CS disable  
	return Temp;
}   

/**
 * @brief flash read single-SPI mode.
 * @param pBuffer: pointer for data buffer.
 * @param ReadAddr: Start address for reading data.
 * @param NumByteToRead: Number byte to be read.
 * @return This function has no return.
 */
void P25Q80L_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{ 
 	uint16_t i;   										    
	P25Q80L_CS_ON;                            		//CS en
    SPI1_ReadWriteByte(P25Q80L_ReadData);         	//read cmd 0x03
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //3-Byte address
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);    
    }
	P25Q80L_CS_OFF;  // CS dis   
}  

/**
 * @brief flash read Dual-SPI mode.
 * @param pBuffer: pointer for data buffer.
 * @param ReadAddr: Start address for reading data.
 * @param NumByteToRead: Number byte to be read.
 * @return This function has no return.
 */
void P25Q80L_DualRead(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{
  	uint16_t i;   										    
	P25Q80L_CS_ON;       //CS en
    SPI1_ReadWriteByte( P25Q80L_FastReadDual);         //read cmd 0x3B
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));   //3-Byte address 
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)ReadAddr);   
	// 1 dummy clock
	SPI1_ReadWriteByte(DUMMY);
	qspi_switch_2line(); 	// Putting the API here for beginning transmit to 2 line mode NOW.
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);   	//循环读数  data reading
    }
	P25Q80L_CS_OFF;
}  

/**
 * @brief flash read Quad-SPI mode.
 * @param pBuffer: pointer for data buffer.
 * @param ReadAddr: Start address for reading data.
 * @param NumByteToRead: Number byte to be read.
 * @return This function has no return.
 */
void P25Q80L_QuadRead(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{ 

	uint16_t i;
	/* The Write Status Register (WRSR) command allows new values to be written to the Status Register. 
	 * Before it can be accepted, a Write Enable (WREN) command must previously have been executed.
	 */
	P25Q80L_Write_Enable();	// write enable.The Write Enable (WREN) command is for setting the Write Enable Latch (WEL) bit.
	// P25Q80L_Wait_WEL(); // not need
	Enable_QE();

#if 0
	/*0xEBh:Quad I/O Fast Read*/
	P25Q80L_CS_ON;      //CS en
    SPI1_ReadWriteByte( Fast_Read_Quad);   //sending read cmd 0xEB
    qspi_switch_4line(); 	// Putting the API here for beginning transmit to 4 line mode NOW.
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //3-Byte address  
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)ReadAddr);
    // 4 dummy clocks
    SPI1_ReadWriteByte(DUMMY);	// dummy
    SPI1_ReadWriteByte(DUMMY);	// dummy
    SPI1_ReadWriteByte(DUMMY);	// dummy
    SPI1_ReadWriteByte(DUMMY);	// dummy
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);   	// 
    }
	P25Q80L_CS_OFF;  	
#endif

#if 1
	P25Q80L_CS_ON;      //CS en
	/*0x6Bh:Quad Output Fast Read*/
	SPI1_ReadWriteByte(0x6B);      //sending read cmd 0x6B
	// qspi_switch_4line();	// should not put here.
	SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));   //3-Byte address  
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)ReadAddr); 
	
	// 1 dummy clock
	SPI1_ReadWriteByte(DUMMY);
	
	qspi_switch_4line();	                  // Putting this API here for beginning transmit to 4 line mode now.

	for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF); // 
    }
	P25Q80L_CS_OFF;  
#endif
}  

void P25Q80L_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
 	uint16_t i;  
    P25Q80L_Write_Enable();                  	     //SET WEL 
	P25Q80L_CS_ON;                            	     //CS enable   
    SPI1_ReadWriteByte(P25Q80L_PageProgram);         //sending Page Program command
    SPI1_ReadWriteByte((uint8_t)((WriteAddr)>>16));  //3-Byte address on SI
    SPI1_ReadWriteByte((uint8_t)((WriteAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)
		SPI1_ReadWriteByte(pBuffer[i]); 
	P25Q80L_CS_OFF;                            	     //CS disable  
	P25Q80L_Wait_Busy();					         // 
} 

void P25Q80L_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
{ 			 		 
	uint16_t pageremain;	   
	pageremain=256-WriteAddr%256; //	 	    
	if(NumByteToWrite<=pageremain)
		pageremain=NumByteToWrite;//
	while(1)
	{	   
		P25Q80L_Write_Page(pBuffer,WriteAddr,pageremain);
		if(NumByteToWrite==pageremain)break;//
		else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;
			WriteAddr+=pageremain;	

			NumByteToWrite-=pageremain;			  //			
			if(NumByteToWrite>256)pageremain=256; //	
			else pageremain=NumByteToWrite; 	  //
		}
	}	    
} 


uint8_t P25Q80L_BUFFER[4096];		 
void P25Q80L_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
{ 
	uint32_t secpos;
	uint16_t secoff;
	uint16_t secremain;	   
 	uint16_t i;    
	uint8_t * P25Q80L_BUF;	  
   	P25Q80L_BUF=P25Q80L_BUFFER;	     
 	secpos=WriteAddr/4096;
	secoff=WriteAddr%4096;
	secremain=4096-secoff;
 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);
	if(NumByteToWrite<=secremain)
	secremain=NumByteToWrite;	
	while(1) 
	{	
		P25Q80L_Read(P25Q80L_BUF,secpos*4096,4096);	
		for(i=0;i<secremain;i++) 
		{
			if(P25Q80L_BUF[secoff+i]!=0XFF)
				break;  
		}
		if(i<secremain) 
		{
			P25Q80L_Erase_Sector(secpos); 
			for(i=0;i<secremain;i++) 
			{
				P25Q80L_BUF[i+secoff]=pBuffer[i];	  
			}
			P25Q80L_Write_NoCheck(P25Q80L_BUF,secpos*4096,4096); 

		}
		else 
			P25Q80L_Write_NoCheck(pBuffer,WriteAddr,secremain); 				   
		if(NumByteToWrite==secremain)
			break; 
		else 
		{
			secpos++; 
			secoff=0; 

			pBuffer+=secremain;  				 
			WriteAddr+=secremain;				 	   
			NumByteToWrite-=secremain;		 
			if(NumByteToWrite>4096)secremain=4096; 
			else secremain=NumByteToWrite;		 
		}	 
	}	 
}

//Chip Erase 
void P25Q80L_Erase_Chip(void)   
{                                   
    P25Q80L_Write_Enable();                 	 	//SET WEL 
    P25Q80L_Wait_Busy();   
  	P25Q80L_CS_ON;                            	 
    SPI1_ReadWriteByte(P25Q80L_ChipErase);         
	P25Q80L_CS_OFF;                                  
	P25Q80L_Wait_Busy();   				   	 
}   

//Sector erase 
void P25Q80L_Erase_Sector(uint32_t Dst_Addr)   
{  
// 	printf("fe:0x%x\r\n",Dst_Addr);
 	Dst_Addr*=4096;
    P25Q80L_Write_Enable();                  	    //SET WEL 	 
    P25Q80L_Wait_Busy();   						    //read flash status (WIP:check busy or not)
  	P25Q80L_CS_ON;                            	    //CS enable   
    SPI1_ReadWriteByte(P25Q80L_SectorErase);      	//sending Sector Erase command
    SPI1_ReadWriteByte((uint8_t)((Dst_Addr)>>16));  //3-Byte address on SI
    SPI1_ReadWriteByte((uint8_t)((Dst_Addr)>>8));   
    SPI1_ReadWriteByte((uint8_t)Dst_Addr);  
	P25Q80L_CS_OFF;                            	   //CS disable    	      
    P25Q80L_Wait_Busy();   				   		 
}  

 
void P25Q80L_Wait_Busy(void)   
{   //bit0:WIP,The Write in Progress (WIP) bit indicates whether the memory is busy in program/erase/write status register progress.
	//1-Y;0-N
	while((P25Q80L_ReadSR()&0x01)==0x01);  	
}   

 
void P25Q80L_Wait_WEL(void)   
{   
	while((P25Q80L_ReadSR()&0x02)==0x00);  		 
}   


void Enable_QE(void)
{
	P25Q80L_Write_SR2(0x2);	// bit1(QE)=1; enable quad-spi 
	P25Q80L_Wait_Busy(); 
}

void qspi_switch_4line(void)
{
	// REG32(SPI_FMT_ADDR) &= ~(0x3);
	// REG32(SPI_FMT_ADDR) |= (0x2);

	QSPI_REG(SPI_REG_FMT) &= ~(0x3);
	QSPI_REG(SPI_REG_FMT) |= (0x2);
}


void qspi_switch_2line(void)
{
	QSPI_REG(SPI_REG_FMT) &= ~(0x3);
	QSPI_REG(SPI_REG_FMT) |= (0x1);
}

















